package regexInput;
import java.util.ArrayList;

public class NFA {
	private ArrayList<ArrayList<ArrayList<Integer>>> table;
	private String[] identifiers;
	private ArrayList<Integer> finalStates;
	
	public ArrayList<ArrayList<ArrayList<Integer>>> getTable(){
		return table;
	}
	
	public NFA(String[] identifiers){
		table = new ArrayList<ArrayList<ArrayList<Integer>>>();
		this.identifiers = identifiers;
		finalStates = new ArrayList<Integer>();
	}
	public ArrayList<ArrayList<Integer>> getState(int state){
		return table.get(state);
	}
	public ArrayList<Integer> getTransition(int state, int identifierRow){
		return table.get(state).get(identifierRow);
	}
	
	public ArrayList<ArrayList<Integer>> createEmptyState(){
		ArrayList<ArrayList<Integer>> state = new ArrayList<ArrayList<Integer>>(); 
		ArrayList<Integer> array = new ArrayList<Integer>();
		array.add(-1);
		for (int i = 0; i <identifiers.length; i ++){
			array = new ArrayList<Integer>();
			array.add(-1);
			state.add(array);
		}
		return state;
	}
	
	public NFA create$(String identifier){
		NFA newNFA = new NFA(identifiers);
		newNFA.getTable().add(createEmptyState());
		//sets the transition to state 2
		if (newNFA.getTable().get(0).get(findIdentifier(identifier)).get(0) == -1)
			newNFA.getTable().get(0).get(findIdentifier(identifier)).set(0,2);
		else
			newNFA.getTable().get(0).get(findIdentifier(identifier)).add(2);
		return newNFA;
	}
	
	public NFA addNewNFA(NFA newNFA){
		boolean create = false;
		for (int i = 0; i < this.getTable().get(0).size()-1;i++){
			if ( this.getTable().get(0).get(i).get(0) > -1 ){
				create = true;
			}
		}
		if (create){
			this.getTable().add(0,createEmptyState());
			this.getTable().get(0).get(findIdentifier("E")).set(0, 2);
			for (int i = 1; i < this.getTable().size();i++){
				for(int j = 0; j < this.getTable().get(i).size();j++){
					for (int k = 0; k < this.getTable().get(i).get(j).size();k++)
						if ( this.getTable().get(i).get(j).get(0) > -1 ){
							this.getTable().get(i).get(j).set(k, this.getTable().get(i).get(j).get(k)+1);
						}
				}
			}
			this.getFinalStates().clear();
			this.getFinalStates().add(this.getTable().size());
		}
		
		
		int offset = this.getTable().size();
		int size = this.getTable().size();
		if (this.getTable().get(0).get(findIdentifier("E")).get(0)==-1){
			this.getTable().get(0).get(findIdentifier("E")).set(0,size+1);
		}
		else
			this.getTable().get(0).get(findIdentifier("E")).add(size+1);
		for (int i = 0; i < newNFA.getTable().size();i++){
			this.getTable().add(createEmptyState());
			for (int j = 0; j < newNFA.getTable().get(i).size();j++){
				for (int k = 0; k < newNFA.getTable().get(i).get(j).size();k++){
					if ((newNFA.getTable().get(i).get(j).get(k) != -1)){
						if ((this.getTable().get(size + i).get(j).get(0)==-1))
							this.getTable().get(size + i).get(j).remove(0);
							this.getTable().get(size + i).get(j).add(newNFA.getTable().get(i).get(j).get(k)+offset);
					}
				}
			}
		}
		
		return this;
	}
	
	public void addNFAOr(NFA newNFA,int firstState){
		int state = this.getTable().size();
		if (firstState <0)
			firstState++;
			
		for (int i = 0; i < newNFA.getTable().get(0).size(); i++){
			for (int j = 0; j < newNFA.getTable().get(0).get(i).size();j++){
				if (newNFA.getTable().get(0).get(i).get(j) > -1){
					this.getTable().get(firstState-1).get(i).set(j, newNFA.getTable().get(0).get(i).get(j)+firstState-1);
				}
			}
		}
		for (int i = 0; i < newNFA.getTable().get(0).size(); i++){
			for (int j = 0; j < newNFA.getTable().get(0+1).get(i).size();j++){
				if (newNFA.getTable().get(0+1).get(i).get(j) > -1){
					this.getTable().get(this.table.size()-1).get(i).set(j, newNFA.getTable().get(0+1).get(i).get(j)+this.table.size()-2);
				}
			}
		}
		for (int i = 2; i < newNFA.getTable().get(0).size(); i++){
			for (int j = 0; j < newNFA.getTable().get(0+1).get(i).size();j++){
				if (newNFA.getTable().get(0+1).get(i).get(j) > -1){
					this.getTable().get(this.table.size()-1).get(i).add(j, newNFA.getTable().get(0+1).get(i).get(j)+this.table.size()-2);
				}
			}
		}
	}
	
	public void addNFAStar(int firstState,int lastState){
		if (table.get(firstState-1).get(findIdentifier("E")).get(0) == -1){
			table.get(firstState-1).get(findIdentifier("E")).set(0, lastState);
		}
		else{
			table.get(firstState-1).get(findIdentifier("E")).add(lastState);
		}
		if (table.get(lastState-1).get(findIdentifier("E")).get(0) == -1){
			table.get(lastState-1).get(findIdentifier("E")).set(0, firstState);
		}
		else
			table.get(lastState-1).get(findIdentifier("E")).add(firstState);
		
	}
	
	public void addNFAPlus(int firstState, int lastState){
		int size = table.size()-1;
		table.remove(size);
		int counter = 0;
		for (int i = firstState-1;i < size;i++){
			table.add(createEmptyState());
			for (int j = 0; j<table.get(0).size();j++){
				if (table.get(i).get(j).get(0) > -1)
					table.get(table.size()-1).get(j).set(0,table.get(i).get(j).get(0)+(table.size()-1-i));
				else
					table.get(table.size()-1).get(j).set(0,table.get(i).get(j).get(0));
			}
			counter++;
		}
		table.add(createEmptyState());
		addNFAStar(lastState,lastState+counter);
	}
	
	public void addNFA$(NFA newNFA){
		ArrayList<Integer> eps = new ArrayList<Integer>();
		int saveState = table.size()-1;
		//saves data that will be deleted
		if (table.size()>0){
			eps = table.get(table.size()-1).get(findIdentifier("E"));
			table.remove(table.size()-1);
		}
		if (newNFA.getTable().size()>1)
			newNFA.getTable().remove(newNFA.getTable().size()-1);
		int offset = this.getTable().size();
		//states
		for (int i = 0; i<newNFA.getTable().size();i++){
			//iden
			for (int j = 0; j<newNFA.getTable().get(i).size();j++){
				//translations
				int size = newNFA.getTable().get(i).get(j).size();
				for (int k = 0; k < size; k++){
					//if not empty
					if (newNFA.getTable().get(i).get(j).get(k) > -1){
						newNFA.getTable().get(i).get(j).set(k,newNFA.getTable().get(i).get(j).get(k)+offset);
					}
				}
			}
			table.add(newNFA.getTable().get(i));
		}
		//re adds deleted epsilon
		if (eps.size()>0){
			if (eps.get(0) > -1){
				table.get(saveState).get(findIdentifier("E")).add(eps.get(0));
			}
		}
		table.add(createEmptyState());
	}
	
	
	public int findIdentifier(String s){
		for (int i = 0;i<identifiers.length;i++){
			if (identifiers[i].equals(s)){
				return i;
			}
		}
		return 0;
	}
	
	public String toString(){
		String s = "";
		for (int i = 0; i < identifiers.length; i++){
			s+=identifiers[i] + "\t";
		}
		s+="\n";
		//states 
		for (int i = 0; i<table.size();i++){
			//iden
			for (int j = 0; j<table.get(i).size();j++){
				//translations
				s+= "[";
				for (int k = 0; k < table.get(i).get(j).size(); k++){
					s += table.get(i).get(j).get(k).toString() + ",";
				}
				s+= "]\t";
			}
			s+="\n";
			
		}
		s+="The final States are: ";
		for (int i = 0; i <finalStates.size();i++){
			s+=finalStates.get(i) + " ";
		}
		return s;
	}
	public ArrayList<Integer> getFinalStates(){
		return finalStates;
	}
	
	public ArrayList<String> finalsToStringArray(){
		ArrayList<String> s = new ArrayList<String>();
		for(int i = 0; i < finalStates.size(); i++){
			s.add((finalStates.get(i) - 1) +"");
		}
		return s;
	}
	public ArrayList<ArrayList<ArrayList<String>>> tableToStringArray(){
		ArrayList<ArrayList<ArrayList<String>>> list = new ArrayList<ArrayList<ArrayList<String>>>();
		
		for (int i = 0; i < table.size();i++){
			list.add(new ArrayList<ArrayList<String>>());
			for (int j = 0;j<table.get(i).size();j++){
				list.get(i).add(new ArrayList<String>());
				for (int k = 0; k <table.get(i).get(j).size();k++){
					if (table.get(i).get(j).get(k) == -1)
						list.get(i).get(j).add(table.get(i).get(j).get(k)+"");
					else
						list.get(i).get(j).add(table.get(i).get(j).get(k)-1+"");
				}
			}
		}
		return list;
	}
	public ArrayList<ArrayList<ArrayList<String>>> exportFormat(){
		ArrayList<ArrayList<ArrayList<String>>> tmp = this.tableToStringArray();
		ArrayList<String> s = new ArrayList<String>();
		for (int i = 0; i < tmp.size();i++){
			s = new ArrayList<String>();
			s.add(0, i+1+"");
			tmp.get(i).add(0,s);
		}
		return tmp;
	}
}
